package network

import (
	"gitee.com/amoyx/cloud-provider/pkg/cloud/tencent/models"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	tvpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
)

type RouteGreeter interface {
	Routes(region string) RouteInterface
}

type RouteInterface interface {
	Create(in models.RouteTableRequest) (result models.CreateResponseParams, err error)
	Get(id string) (result models.RouteTableResponse, err error)
	ListByVpc(vpcId string) (list []models.RouteTableResponse, err error)
	ListByName(name string) (list []models.RouteTableResponse, err error)
	List() (list []models.RouteTableResponse, err error)
	Delete(id string) error
	Update(routeTableId string, routeTableName string, args []models.RoutePolicy) (err error)
	DeleteRoutePolicy(routeTableId string, policyId uint64) (err error)
	CreateRoutePolicy(routeTableId string, args []models.RoutePolicy) (result models.CreateResponseParams, err error)
	UpdateSubnetAssociation(routeTableId string, subnetId string) (err error)
}

type route struct {
	network *Network
	region  string
	client  *tvpc.Client
}

func newRoute(network *Network, region string) RouteInterface {
	v := &route{
		network: network,
		region:  region,
	}
	cpf := profile.NewClientProfile()
	cpf.HttpProfile.Endpoint = string(VpcEndPoint)
	client, _ := tvpc.NewClient(v.network.Credential(), v.region, cpf)
	v.client = client
	return v
}

// Create 创建路由表
func (r *route) Create(in models.RouteTableRequest) (result models.CreateResponseParams, err error) {
	request := tvpc.NewCreateRouteTableRequest()
	request.VpcId = common.StringPtr(in.VpcId)
	request.RouteTableName = common.StringPtr(in.RouteTableName)
	var tags []*tvpc.Tag
	for key, val := range in.Tags {
		tags = append(tags, &tvpc.Tag{
			Key:   common.StringPtr(key),
			Value: common.StringPtr(val),
		})
	}
	request.Tags = tags
	response, err := r.client.CreateRouteTable(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	result.InstanceSet = []string{*response.Response.RouteTable.RouteTableId}
	return result, nil
}

// Update 更新路由表
func (r *route) Update(routeTableId string, routeTableName string, args []models.RoutePolicy) (err error) {
	request := tvpc.NewResetRoutesRequest()
	request.RouteTableId = common.StringPtr(routeTableId)
	request.RouteTableName = common.StringPtr(routeTableName)
	var policies []*tvpc.Route
	for _, row := range args {
		policies = append(policies, &tvpc.Route{
			DestinationCidrBlock: common.StringPtr(row.DestinationCidrBlock),
			GatewayType:          common.StringPtr(row.GatewayType),
			GatewayId:            common.StringPtr(row.GatewayId),
			RouteDescription:     common.StringPtr(row.RouteDescription),
			Enabled:              common.BoolPtr(row.Enabled),
			RouteType:            common.StringPtr(row.RouteType),
			RouteTableId:         common.StringPtr(routeTableId),
		})
	}
	request.Routes = policies
	_, err = r.client.ResetRoutes(request)
	return
}

func (r *route) UpdateSubnetAssociation(routeTableId string, subnetId string) (err error) {
	request := tvpc.NewReplaceRouteTableAssociationRequest()
	request.SubnetId = common.StringPtr(subnetId)
	request.RouteTableId = common.StringPtr(routeTableId)
	_, err = r.client.ReplaceRouteTableAssociation(request)
	return
}

// DeleteRoutePolicy 删除路由策略
func (r *route) DeleteRoutePolicy(routeTableId string, policyId uint64) (err error) {
	request := tvpc.NewDeleteRoutesRequest()
	request.RouteTableId = common.StringPtr(routeTableId)
	request.Routes = []*tvpc.Route{
		&tvpc.Route{
			RouteId: common.Uint64Ptr(policyId),
		},
	}
	_, err = r.client.DeleteRoutes(request)
	return
}

// CreateRoutePolicy 创建路由策略
func (r *route) CreateRoutePolicy(routeTableId string, args []models.RoutePolicy) (result models.CreateResponseParams, err error) {
	request := tvpc.NewCreateRoutesRequest()
	request.RouteTableId = common.StringPtr(routeTableId)
	var policies []*tvpc.Route
	for _, row := range args {
		policies = append(policies, &tvpc.Route{
			DestinationCidrBlock: common.StringPtr(row.DestinationCidrBlock),
			GatewayType:          common.StringPtr(row.GatewayType),
			GatewayId:            common.StringPtr(row.GatewayId),
			RouteDescription:     common.StringPtr(row.RouteDescription),
			Enabled:              common.BoolPtr(row.Enabled),
			RouteType:            common.StringPtr(row.RouteType),
			RouteTableId:         common.StringPtr(routeTableId),
		})
	}
	request.Routes = policies
	response, err := r.client.CreateRoutes(request)
	result.RequestId = *response.Response.RequestId
	var ids []string
	for _, row := range response.Response.RouteTableSet {
		ids = append(ids, *row.RouteTableId)
	}
	result.InstanceSet = ids
	return
}

// Delete 删除路由表
func (r *route) Delete(id string) error {
	request := tvpc.NewDeleteRouteTableRequest()
	request.RouteTableId = common.StringPtr(id)
	_, err := r.client.DeleteRouteTable(request)
	return err
}

func (r *route) Get(id string) (result models.RouteTableResponse, err error) {
	list, err := r.query(map[string]string{"route-table-id": id})
	if len(list) > 0 {
		return list[0], nil
	}
	return
}

func (r *route) ListByVpc(vpcId string) (list []models.RouteTableResponse, err error) {
	return r.query(map[string]string{"vpc-id": vpcId})
}

func (r *route) ListByName(name string) (list []models.RouteTableResponse, err error) {
	return r.query(map[string]string{"route-table-name": name})
}

func (r *route) List() (list []models.RouteTableResponse, err error) {
	return r.query(map[string]string{})
}

func (r *route) query(args map[string]string) (list []models.RouteTableResponse, err error) {
	request := tvpc.NewDescribeRouteTablesRequest()
	var filters []*tvpc.Filter
	for key, val := range args {
		filters = append(filters, &tvpc.Filter{
			Name:   common.StringPtr(key),
			Values: common.StringPtrs([]string{val}),
		})
	}
	request.Offset = common.StringPtr("0")
	request.Limit = common.StringPtr("100")
	response, err := r.client.DescribeRouteTables(request)
	if err != nil {
		return
	}
	for _, row := range response.Response.RouteTableSet {
		var subnetIds []string
		for _, sub := range row.AssociationSet {
			subnetIds = append(subnetIds, *sub.SubnetId)
		}
		var policies []models.RoutePolicy
		for _, py := range row.RouteSet {
			policies = append(policies, models.RoutePolicy{
				DestinationCidrBlock:     *py.DestinationCidrBlock,
				GatewayType:              *py.GatewayType,
				GatewayId:                *py.GatewayId,
				RouteId:                  *py.RouteId,
				RouteDescription:         *py.RouteDescription,
				Enabled:                  *py.Enabled,
				RouteType:                *py.RouteType,
				RouteTableId:             *py.RouteTableId,
				DestinationIpv6CidrBlock: *py.DestinationIpv6CidrBlock,
				RouteItemId:              *py.RouteItemId,
				PublishedToVbc:           *py.PublishedToVbc,
				CreatedTime:              *py.CreatedTime,
			})
		}
		list = append(list, models.RouteTableResponse{
			VpcId:          *row.VpcId,
			RouteTableId:   *row.RouteTableId,
			RouteTableName: *row.RouteTableName,
			Main:           *row.Main,
			RoutePolicy:    policies,
			SubnetIds:      subnetIds,
			CreatedTime:    *row.CreatedTime,
		})
	}
	return
}
